package datawave.xmlsync;

import java.util.Stack;

import javax.xml.transform.TransformerException;

import org.apache.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import xmlsync2.Synchronizer;
import xmlsync2.XMLAPI;
import xmlsync2.XmlsyncException;

import datawave.util.LoggerCache;
import datawave.xsltcompiler.XSLTCompiler;
import datawave.xsltcompiler.XSLTCompilerException;

public class XMLSync implements Machine
{
	protected Stack stack = null;
	private static Logger log = LoggerCache.get(Synchronizer.class.getName());

	/**
	 * Constructor for XMLSync.
	 */
	public XMLSync()
	{
		stack = new Stack();
	}

	public void set(final String xpath, final String value) throws MachineException
	{
		try
		{
			if (stack.empty())
				stack.push(XMLAPI.newdocument());
			XMLAPI.set((Document)stack.peek(), xpath, value);
		}
		catch (XmlsyncException e){
			throw new MachineException("[Machine.set] source : " + e.getMessage());
		}
	}

	public void setxml(final String xpath, final String value) throws MachineException
	{
		try
		{
			if (stack.empty() && xpath.equals("/"))
			{
				stack.push(XMLAPI.loadxml(value));
				return;
			}
			else if (stack.empty()) stack.push(XMLAPI.newdocument());
			XMLAPI.setxml((Document)stack.peek(), xpath, XMLAPI.loadxml(value));
		}
		catch (XmlsyncException e)
		{
			throw new MachineException("[Machine.setxml] source : " + e.getMessage());
		}
	}

	public String get(final String xpath) throws MachineException
	{
		try
		{
			if (stack.empty())	return null;
			else
			{
				Document doc = (Document) stack.peek();
				if (doc == null) return "";
				else
				{
					String value = XMLAPI.valueof(doc, xpath);
					if (value == null)
						return "";
					else return value;	
				}
			}
		}
		catch (XmlsyncException e)
		{
			throw new MachineException("[Machine.get] source : " + e.getMessage());
		}
	}
	
	public String getxml(final String xpath) throws MachineException
	{
		try
		{
			if (stack.empty()) return null;
			else
			{
				Document doc = (Document)stack.peek();
				if (doc == null) return "";
				else
				{
					NodeList list = XPathAPI.selectNodeList(doc, xpath);
					String str = "";
					for (int i = 0; i < list.getLength(); i++)
						str += XMLAPI.xml(list.item(i));
					return str;
				}
			}
		}
		catch (TransformerException e)
		{
			throw new MachineException("[Machine.getxml] source : " + e.getMessage());
		}
	}

	public void load(final String filepath) throws MachineException
	{
		try
		{
			stack.push(XMLAPI.load(filepath));
		}
		catch (XmlsyncException e)
		{
			throw new MachineException("[Machine.load] source : " + e.getMessage());
		}
	}
	
	public void run(final String scriptpath) throws MachineException
	{
		try
		{
			Synchronizer xmlsync = new Synchronizer(scriptpath);
			Document input = null;
			if (!stack.empty())
				input = (Document) stack.peek();
			stack.push(xmlsync.run(input));
		}
		catch (Exception e)
		{
			throw new MachineException("[Machine.run] source : " + e.getMessage());
		}		
	}
	
	public void pop()
	{
		if (stack.empty()) return;
		else stack.pop();
	}
	
	public boolean empty()
	{
		return stack.empty();
	}
	
	public void push() throws MachineException
	{
		try
		{
			stack.push(XMLAPI.newdocument());
		}
		catch (XmlsyncException e)
		{
			throw new MachineException("[Machine.push] source : " + e.getMessage());
		}
	}

	public String transform(final String scriptpath) throws MachineException
	{
		log.info("machine.transform : "+scriptpath);
		if (stack.empty())
			throw new MachineException("[Machine.transform] Stack is empty. Machine cannot transform without input xml.");
		else
		{
			try
			{
				return XSLTCompiler.transform(scriptpath, (Document)stack.peek());
			}
			catch (XSLTCompilerException e)
			{
				throw new MachineException("[Machine.transform] source : " + e.getMessage());
			}
		}
	}
	
	public Node getnode(final String xpath) throws MachineException
	{
		try
		{
			if (stack.empty()) return null;
			else
			{
				Document doc = (Document)stack.peek();
				if (doc == null) return null;
				else
				{
					return XPathAPI.selectSingleNode(doc, xpath);
				}
			}
		}
		catch (TransformerException e)
		{
			throw new MachineException("[Machine.getnode] source : " + e.getMessage());
		}
	}
}
